
package fuzzyLogic;

import java.util.*;

/**
 * Klasa określająca zmienną lingwistyczną wraz z jej wyszytkimi zbiorami.
 * @author CygiDawid
 */
public class LinguisticVariable {
    private final String _name;
    private final int _from ;
    private final int _to;
    private final int _step;
    private final double _stepDiv;  // = 1 / _step   potrzebne do wyliczeń ze względu na to, że mnożenie jest mniej zasobo-żerne niż dzielenie
    
    private final int _valuesCount;
    private final ArrayList<double[]> _sets;
    
    
    /**
     * Tworzy nową zmienną lingwistyczną na podstawie danych wejściowych
     * @param name Nazwa zmiennej
     * @param from Wartość minimalna
     * @param to Wartość maksymalna
     * @param step Krok o jaki wartości mają się zmieniać
     */
    public LinguisticVariable(String name, int from, int to, int step) {
        _name = name;
        _from = from;
        _to = to;
        _step = step;
        _stepDiv = 1d / (double)_step;
        
        
        _valuesCount = ((_to - _from) / _step) + 1;
        _sets = new ArrayList();
    }
    
    
    /**
     * "Getter" nazwy zmiennej lingwistycznej
     * @return 
     */
    public String getName() {
        return _name;
    }
    
    /**
     * Dodaje term w postaci trójkąta do zmiennej lingwistycznej
     * @param from Początek termu
     * @param mid Punkt środkowy termu
     * @param to Koniec termu
     */
    public void addSet(int from, int mid, int to) {
        double[] result = new double[_valuesCount];
        for (int i = 0; i < _valuesCount; i++) {
            int value = _from + _step * i;
            
            if (value <= from || value >= to) {
                result[i] = 0d;
            }
            else if (value > from && value <= mid) {
                result[i] = (value - from) / (double)(mid - from);
            }
            else {
                result[i] = (to - value) / (double)(to - mid);
            }
        }
        
        _sets.add(result);
    }
    
    /**
     * Dodaje term w postaci trapezu do zmiennej lingwistycznej
     * @param from Początek termu
     * @param mid1 Pierwszy punkt środkowy
     * @param mid2 Drugi punkt środkowy
     * @param to Koniec termu
     */
    public void addSet(int from, int mid1, int mid2, int to) {
        double[] result = new double[_valuesCount];
        for (int i = 0; i < _valuesCount; i++) {
            int value = _from + _step * i;
            
            if (value <= from || value >= to) {
                result[i] = 0d;
            }
            else if (value > from && value < mid1) {
                result[i] = (value - from) / (double)(mid1 - from);
            }
            else if (value > mid2 && value < to) {
                result[i] = (to - value) / (double)(to - mid2);
            }
            else {
                result[i] = 1d;
            }
        }
        
        _sets.add(result);
    }
    
    /**
     * "Getter" ilości termów w zmiennej lingwistycznej
     * @return Ilość termów
     */
    public int getSetsCount() {
        return _sets.size();
    }
    
    /**
     * Pobiera przynależność podanej wartości do poszczególnych
     * termów (w kolejności dodawania termów)
     * @param value Wartość wejściowa
     * @return Przynależność podanej wartości
    */
    public double[] getAffiliations(int value) {
        double[] result = new double[_sets.size()];
        int idx = (int)((value - _from) * _stepDiv);
        
        for (int i = 0; i < _sets.size(); i++) {
            result[i] = _sets.get(i)[idx];
        }
        
        return result;
    }
    
    
    /**
     * Oblicza wartość wyjściową metodą środka ciężkości
     * @param setsMaxValues Przycięcia termów
     * @return Wartość wyjściowa
     */
    public double getCenterOfGravity(double[] setsMaxValues) {
        double centerUp = 0;
        double centerDown = 0;
        
        for (int i = 0; i < _valuesCount; i++) {
            double result = Math.min(_sets.get(0)[i], setsMaxValues[0]);
            for (int j = 1; j < _sets.size(); j++) {
                double setAffiliation = Math.min(_sets.get(j)[i], setsMaxValues[j]);
                result = Math.max(result, setAffiliation);
            }
            
            centerUp += result * (_from + i * _step);
            centerDown += result;
        }
        
        if (centerDown == 0) {
            return 0d;
        }
        return centerUp / centerDown;
    }
}
